<?php

namespace App\Http\Common;
use App\Http\Controllers\Admin\tool\LogController;
use App\Models\Front\Previews;
use App\Models\Front\SearchHistory;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Yankewei\LaravelSensitive\Sensitive;
use function EasyWeChat\Kernel\Support\get_client_ip;
class MyUtils{
    //创建静态私有的变量保存该类对象
    private static $instance;
    //参数
    private $config;
    //防止直接创建对象
    private function __construct($config){
        $this->config = $config;
    }
    //防止克隆对象
    private function __clone(){
    }
    // 使用单列模式
    public static function getInstance($config=null){
        //判断$instance是否是Uni的对象
        //没有则创建
        if (!self::$instance instanceof self) {
            self::$instance = new self($config);
        }
        return self::$instance;
    }
    /**
     * 加密解密函数
     * @param $string 要加密、解密的数据
     * @param string $operation DECODE解密/ENCODE加密
     * @param string $key 加密解密密码
     * @param int $expiry 过期时间 过了一个小时这个 加密的字符就不能被解密了。
     * @return bool|string
     */
    public static function authcode($string, $operation = 'DECODE', $key = '', $expiry = 3600) {
        if (!$key){
            $key = env('CHARGE_KEY').Session::get('token');
        }
        $ckey_length = 4;
        // 随机密钥长度 取值 0-32;
        // 加入随机密钥，可以令密文无任何规律，即便是原文和密钥完全相同，加密结果也会每次不同，增大破解难度。
        // 取值越大，密文变动规律越大，密文变化 = 16 的 $ckey_length 次方
        // 当此值为 0 时，则不产生随机密钥

        $key = md5($key ? $key : 'default_key'); //这里可以填写默认key值
        $keya = md5(substr($key, 0, 16));
        $keyb = md5(substr($key, 16, 16));
        $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';

        $cryptkey = $keya.md5($keya.$keyc);
        $key_length = strlen($cryptkey);

        $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
        $string_length = strlen($string);

        $result = '';
        $box = range(0, 255);

        $rndkey = array();
        for($i = 0; $i <= 255; $i++) {
            $rndkey[$i] = ord($cryptkey[$i % $key_length]);
        }

        for($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $rndkey[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }

        for($a = $j = $i = 0; $i < $string_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $result .= ''.chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }

        if($operation == 'DECODE') {
            if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
                return substr($result, 26);
            } else {
                return '';
            }
        } else {
            return $keyc.str_replace('=', '', base64_encode($result));
        }
    }

    /**
     * 加密解密函数
     * @param $string 要加密、解密的数据
     * @param string $key 加密解密密码
     * @param int $expiry 过期时间 过了一个小时这个 加密的字符就不能被解密了。
     * @return bool|string
     */
    public static function encodeNew($string, $key = '', $expiry = 3600) {
        if (!$key){
            $key = env('CHARGE_KEY').Session::get('token');
        }
        $data = $string.'|'.$key.'|'.time();
        $res = encrypt($data);
        return $res;
    }
    /**
     * 解密解密函数
     * @param $string 要加密、解密的数据
     * @param string $key 加密解密密码
     * @param int $expiry 过期时间 过了一个小时这个 加密的字符就不能被解密了。
     * @return bool|string
     */
    public static function decodeNew($string, $key = '', $expiry = 3600) {
        $string = decrypt($string);
        $res = explode('|',$string);
        if (!count($res)){
            return false;
        }
        if ($key!==$res[1]){
            return false;
        }
        if (time()-$res[2]>$expiry){
            return false;
        }
        return $res[0];
    }


    /**
     * 清除掉内部的列
     * @param $obj 要清除的对象或者数组
     * @param $column 要清除的对象的列
     */
    public static function unsetArrObj(&$obj,$column){
        if (is_object($obj)){
            foreach ($column as $k=>$v){
                unset($obj->$v);
            }
        }
        if (is_array($obj)){
            foreach ($column as $k=>$v){
                unset($obj[$v]);
            }
        }
    }

    /**
     * 加密手机号，将手机号中间变成 137******1234
     * @param $phone
     * @return mixed
     */
    public static function encryptPhone($phone){
        $count = strlen($phone);
        $star = "";
        for($i=0;$i<$count-7;$i++){
            $star .= "*";
        }
        $phone = substr_replace($phone,$star,3,$count-7);
        return $phone;
    }

    /**
     * 拿到需要的控制器
     * @param $mapperRouter 需要获得的控制器的 路由 action
     * @return string
     */
    public static function getController($mapperRouter){
        $router = app('router')->getRoutes();
        $post = $router->get('POST');
        $gets = $router->get('GET');
        $controller = "";
        if (isset($gets[$mapperRouter])){
            $action = $post[$mapperRouter]->action;
            $controller = $action['controller'];
            $controllerArr = explode('\\Controllers\\',$controller);
            $controller = $controllerArr[1];
        };
        if (isset($post[$mapperRouter])){
            $action = $post[$mapperRouter]->action;
            $controller = $action['controller'];
            $controllerArr = explode('\\Controllers\\',$controller);
            $controller = $controllerArr[1];
        }
        return $controller;
    }

    /**
     * 得到数据库可以保存的数据
     * @param $data
     * @return array
     */
    public static function getDbKnowArray($data){
        $res = [];
        if ($data){
            if (is_object($data)){
                $className = get_class($data);
                $pos = strpos($className,'Request');
                if ($pos!==false){
                    $data = $data->request->all();
                }else{// 普通对象
                    $data = self::obj2arr($data);
                }
            }
            foreach ($data as $k=>$v){
                if (is_array($v)){
                    $res[$k] = json_encode($v);
                }else{
                    $res[$k] = $v;
                }
            }
        }
        return $res;
    }
    /**
     * 拿到树形结构 对象的无限级分类
     * @param $data 所有的数据 对象
     * @param $pid 父id
     * @param int $level 深度
     * @return array 数组
     */
    public  static function  getTree($data,$pid,$level=0)
    {
        $tree=[];
        foreach ($data as $row) {
            if($row->parent_id==$pid){
                $row->level=$level;
                $row->children=self::getTree($data,$row->id,$level+1);
                $tree[]=$row;
            }
        }
        return $tree;
    }
    /**
     * 拿到树形结构 数组的无限级分类
     * @param $data 所有的数据 数组
     * @param $pid 父id
     * @param int $level 深度
     * @return array 数组
     */
    public  static function  getTreeArr($data,$pid,$level=0)
    {
        $tree=[];
        foreach ($data as $row) {
            if($row['parent_id']==$pid){
                $row['level']=$level;
                $row['children']=self::getTreeArr($data,$row['id'],$level+1);
                $tree[]=$row;
            }
        }
        return $tree;
    }
    /**
     * 判断是否存在 在数组中 可以实现正则 匹配
     *
     * @param $request
     * @param $arr 数组
     * @return bool
     */
    public static function inExceptArray($request,$arr)
    {
        foreach ($arr as $except) {
            if ($except !== '/') {
                $except = trim($except, '/');
            }
            if ($request->fullUrlIs($except) || $request->is($except)) {
                return true;
            }
        }
        return false;
    }
    /**
     * 将数据进行统一处理，然后传到前端，让前端更能够识别，采用统一的模式
     * @param $data 传递的数据
     * @param int $code 传递的代码 -1代表失败；1代表成功
     * @param array $message 新增的字段在data之外的
     * @return string
     */
    public static function dealFD($data,$code=1,$message=['retMsg'=>'success']){
        if ($code==-1){
            $data = self::getArrayCycle($data);
        }
        if (env('ID_ENCODE_OPEN')){
            if (MyUtils::sg($data,'data') && is_array(MyUtils::sg($data,'data'))) {// 加密数据
                foreach ($data['data'] as $kk => $vv) {
                    if (isset($vv->id) && $vv->id) {
                        $vv->id = self::encodeColumn($vv->id);
                    }
                }
            }
            if (is_array($data)){
                if (MyUtils::sg($data,'id')){
                    $id = self::encodeColumn(MyUtils::sg($data,'id'));
                    $data['id'] = $id;
                }
            }
            if (is_object($data)){
                $id = self::encodeColumn(MyUtils::sg($data,'id'));
                $data->id = $id;
            }
        }
        $res['code'] = $code;
        $res['data'] = $data;
        if (is_array($message) && count($message)) {// 新增的字段在 data之外的
            foreach ($message as $k=>$v){
                $res[$k] = $v;
            }
        }
        return $res;
    }
    public static function df($data,$code=1,$message=['retMsg'=>'success']){
        return self::dealFD($data,$code,$message);
    }

    /**
     * 监听写请求的 日志
     * @param $request
     * @param $data
     */
    public static function listenWriteRequestLog($request,$data){
        if (Globals::$writeLog) {
            if (!$_FILES){
                try{
                    if ($request && isset($request->request)){
                        $controller = $request->url();
                        $request = $request->all();
                    }else{
                        $controller = '';
                        $request = '';
                    }
                    if ($data->exception){
                        $data = $data->exception->getMessage();
                    }else{
                        $data = $data->content();
                    }
                    $path = base_path("storage/logs/request/");
                    // 文件夹不存在就创建
                    if (!file_exists($path))  MyUtils::mkdirs($path);
                    //写入
                    if ($request) {
                        $content = "ip:".get_client_ip()."\n[" . date("Y-m-d H:i:s") . "]" . $controller . "\r\nparmars:" . json_encode($request, 320) . "\r\nresult:".$data."\r\n";
                    } else {
                        $content = "ip:".get_client_ip()."\n[" . date("Y-m-d H:i:s") . "]" . $controller . "\r\nresult:".$data."\r\n";
                    }
//                    if (is_string(Globals::$temp)){
//                        $content .= "unqiueCode: ".Globals::$temp."\r\n";
//                    };
                    file_put_contents($path.date("Y-m-d-H") .'-request.log',$content."\r\n", FILE_APPEND);
                }catch (\Exception $e){
                }
            }
        }
    }
    /**
     * 拿到 $arr 的 所有数组 的 第一个数组 里面的内容，为了拿到错误提醒
     * @param $arr
     * @return mixed
     */
    public static function getArrayCycle($arr){
        $res = $arr;
        if (is_array($arr) && count($arr)){
            $arr = array_values($arr);
            $res = self::getArrayCycle($arr[0]);
        }
        return $res;
    }
    /**
     * 写日志
     * @param $prefix 用户登录等 操作
     * @param $category 普通操作/特殊操作
     * @param $level 等级 1,2,3,4,5,6,7,数字越大等级越低，2代表登录退出操作，3代表员工操作 99代表是调试模式，才能进行
     * @param $message 消息内容 备注信息
     * @return string
     */
    public static function writeLogs ($prefix,$category,$level,$message){
        if ($level == 99 && !env('APP_DEBUG')){
        }else{
//            $controller = new LogController();
//            return $controller->write($prefix,$category,$level,$message);
        }
    }
    /**
     * 无论在windows还是linux里面都能实现后台运行
     * @param $cmd
     */
    public static function execInBackground($cmd) {
        if (substr(php_uname(), 0, 7) == "Windows"){
            $timer =popen("start /B ". $cmd, "r");
            usleep(50);
            pclose($timer);
        }
        else {
            exec($cmd . " > /dev/null &");
        }
    }
    /**
     * 获取圆
     * @param $imgPath 图片网络路径, 本地路径
     * @return resource
     * @author 19/1/29 CLZ.
     */
    public function circleImg($imgPath)
    {
        $src_img = imagecreatefromjpeg($imgPath);

        list($w, $h) = getimagesize($imgPath);
        $w           = $h = min($w, $h);
        $img         = imagecreatetruecolor($w, $h);
        imagesavealpha($img, true);

        // 拾取一个完全透明的颜色,最后一个参数127为全透明
        $bg = imagecolorallocatealpha($img, 255, 255, 255, 127);

        imagefill($img, 0, 0, $bg);
        $r = $w / 2; // 圆的半径
        for ($x = 0; $x < $w; $x++) {
            for ($y = 0; $y < $h; $y++) {
                $rgbColor = imagecolorat($src_img, $x, $y);
                if (((($x - $r) * ($x - $r) + ($y - $r) * ($y - $r)) < ($r * $r)))
                    imagesetpixel($img, $x, $y, $rgbColor);
            }
        }
        imagedestroy($src_img);
        return $img;
    }

    /**
     * 无论在windows还是linux里面都能实现后台运行
     * @param $cmd
     */
    public static function execInFront($cmd) {
        exec($cmd);
    }

    /**
     * 物业内用户的日志的写功能，写日志
     * @param string $do 完成了 未完成
     * @param string $action 管道疏通等
     * @param string $type 维修 审核 等
     * @param string $where $where
     * @param int $level 等级 1,2,3,4,5,6,7,数字越大等级越低，2代表登录退出操作，3代表员工操作
     * @return string
     */
    public static function writeWyLogs ($do="",$action="",$type="",$where="",$level=1){
        $controller = new LogController();
        return $controller->writeWyLogs($where,$level,$do,$action,$type);
    }

    /**
     * 插入数据库
     * @param string $query
     * @param array $bindings
     * @return true false
     */
    public static function dbInsert (string $query, array $bindings = []){
        return DB::insert($query,$bindings);
    }

    /**
     * 清除请求的参数的列
     * @param $request
     * @param bool $setProjectId
     * @return mixed
     */
    public static function clearRequestCloumn($request,$setProjectId=true){
        $res = clone $request;
        foreach ($request->all() as $k=>$v){
            $res->request->remove($k);
        }
        if ($setProjectId)$res->request->set('project_id',self::getSessionConfig('PROJECT_ID'));
        return $res;
    }

    /**
     * 将某个键值一样的数组放在一起
     * @param $arr
     * @param $column
     * @return mixed
     */
    public static function sameArrayAlong($arr,$column){
        $result = [];
        foreach ($arr as $data) {
            isset($result[$data[$column]]) || $result[$data[$column]] = array();
            $result[$data[$column]][] = $data;
        }
        ksort($result);
        return $result;
    }

    /**
     * 设置成功失败 信息
     * @param string $success
     * @param string $error
     */
    public static function setSuccessFaildMessage($success="修改成功！",$error="修改失败！"){
        // 分配订单成功以后就调用 相应的发送模板消息的
        if (!Globals::$success)Globals::$success = $success;
        // 设置失败后返回的字符串
        if (!Globals::$error)Globals::$error = $error;
    }
    /**
     * 设置成功失败 信息
     * @param string $success
     * @param string $error
     */
    public static function setSFM($success="修改成功！",$error="修改失败！"){
        self::setSuccessFaildMessage($success,$error);
    }

    /**
     * 查询数据数据库
     * @param string $query 查询语句
     * @param array $bindings 数据 为数组
     * @param bool $array 为一个时是否返回数组 默认是 当为一条时 不返回数组
     * @return true false
     */
    public static function dbSelect (string $query, $bindings = [],$array = false){
        // 将#projectId# #deletedAt# 进行替换 成sql
        if (Globals::$alias){
            $p = " and ".Globals::$alias.".project_id = '".self::getSessionConfig("PROJECT_ID") . "' ";
            $d = " and ".Globals::$alias.".deleted_at is null ";
        }else{
            $p = " and project_id = '".self::getSessionConfig("PROJECT_ID") . "' ";
            $d = " and deleted_at is null ";
        }
        $query = str_replace(
            ["#projectId#","#deletedAt#","#p#","#d#"]
            ,[$p,$d,$p,$d]
            ,$query
        );
        $query = str_ireplace(
            ["where and","where  and","where   and"]
            ,"where"
            ,$query
        );
        // 判断是否是数组，不是数组就将其变成数组，防止传递参数出错
        if (!is_array($bindings)){
            $bindings = [$bindings];
        }
        if (strpos($query,'limit')===false){// sql排序的规则
            if (Globals::$orderBy){
                if (strpos(Globals::$orderBy,'order by')===false){// 没有order by
                    Globals::$orderBy = " order by ".Globals::$orderBy;
                }
            }
            $query .= ' '.Globals::$orderBy;
        }else{// sql排序的规则
            if (Globals::$orderBy){
                if (strpos(Globals::$orderBy,'order by')===false){// 没有order by
                    Globals::$orderBy = " order by ".Globals::$orderBy;
                }
            }
            $query = str_replace('limit',' '.Globals::$orderBy.' limit',$query);
        }
        /******以下代码实现了当拿到的数据是最新的，就自动从redis取，不是最新的就从mysql里面取******/
        // 查询所有的env('DB_PREFIX') 开头的 所有的表的名称
        self::getTablesArray($query,$tableNames);
        $res = '';// 存储结果的变量
        if (count($tableNames)>0){
            $allTableUpdateTime = self::getAllTableUpdateTime($tableNames);
            // 得到相应sql语句的 更新时间 // 参数以及数据结合
            $md5 = md5(serialize(array_merge([$query],array_filter($bindings))));
            $sqlTableUpdateTime = Redis::get($md5."-update-time")+0;
            // 综合的表更新了，或者 sql语句更新了
            if ($allTableUpdateTime >= $sqlTableUpdateTime || !env("REDIS_OPEN"))
            {
                // 表示数据库表数据更新了,就需要写入数据数据到redis里面,字为query以及参数的md5
                $res = DB::select($query,$bindings);
                // 保存redis完成，就将reids的该表的更新状态写成false
                if (env('REDIS_OPEN')){
                    Redis::set($md5,serialize($res));
                    Redis::set($md5."-update-time",time());
                }
            }else{
                $bindings[] = $query;// 参数以及数据结合
                $res = unserialize(Redis::get($md5));
            }
        }
        /******以上代码实现了当拿到的数据是最新的，就自动从redis取，不是最新的就从mysql里面取******/
        // $array 为true 返回的 就一定是数组
        if(count($res)==1 && !$array){
            return $res[0];
        }else{
            return $res;
        }
    }

    /**
     * 拿到所有表的最近更新时间
     */
    public static function getAllTableUpdateTime($tableNames){
        // reids 里面的一条该标志为true就 标志了该条数据更新了，需要重新查询
        $allTableUpdateTimeArr = [];
        foreach ($tableNames as $k=>$v){
            // 得到每个表的 独自更新情况
            $temp = Redis::get(trim($v)."-table-update-time");
            $allTableUpdateTimeArr[] = $temp;
        }
        // 得到 所有分表 的 最大的更新时间
        if (count($allTableUpdateTimeArr)==0){
            $allTableUpdateTime = 0;
        }else{
            $allTableUpdateTime = max($allTableUpdateTimeArr)+0;
        }
        return $allTableUpdateTime;
    }
    /**
     * 拿到 所有的表数组
     * @param $query
     * @param $tableNames
     */
    public static function getTablesArray($query,& $tableNames){
        preg_match_all("/[\s,\n]*".env('DB_PREFIX')."[\S]*/",$query, $tableNames);
        if (count($tableNames)>0){
            $tableNames = $tableNames[0];
        }
    }
    /**
     * 写数据库的记录
     * @param $query sql语句
     * @param $bindings 需要替换的数组
     */
    public static function dbLog($query,$bindings){
        $temp =[];
        foreach ($bindings as $k=>$v){
            $temp[] = ":".$k;
        }
        $query = str_replace($temp,$bindings,$query);
        $path = "./logs/sql/";
        // 文件夹不存在就创建
        if (!file_exists($path))  MyUtils::mkdirs($path);
        //写入sql
        file_put_contents($path.date("Y-m-d-H") .'-sql.log', "[" . date("Y-m-d H:i:s") . "]" . $query ."\r\n\r\n", FILE_APPEND);
    }

    /**
     * 安全 拿到数组 里面的值 safe get
     * @param mix $arr 要获取的数组
     * @param string $key 要获取的数组的健值
     * @param string $default 默认值
     * @param bool $judgeEmpty true会做空判断
     * @return string
     */
    static function sg ($arr,string $key, $default="",$judgeEmpty=false){
        if (is_array($arr)){
            if ($judgeEmpty){
                return (isset($arr[$key])&&$arr[$key])?$arr[$key]:$default;
            }else{
                return isset($arr[$key])?$arr[$key]:$default;
            }
        }if (is_object($arr)){
            if ($judgeEmpty){
                return (isset($arr->$key)&&$arr->$key)?$arr->$key:$default;
            }else{
                return isset($arr->$key)?$arr->$key:$default;
            }
        }else{
            return $default;
        }
    }
    /**
     * 安全 设置 里面的值 safe set
     * @param array $arr 要设置的数组 对象
     * @param string $key 要获取的数组的健值
     * @param string $default 默认值
     * @return string
     */
    static function ss (&$arr,string $key, $val=""){
        if (is_array($arr)){
            $arr[$key]= $val;
        }if (is_object($arr)){
            $arr->$key=$val;
        }
    }
    /**
     * 将对象转化为数组
     * @param $obj
     * @return mixed
     */
    static function obj2arr ($obj){ return json_decode(json_encode($obj),true); }
    /**
     * 请求验证规则 当  传输过来的表单验证发生问题 就会定位到这里
     * 验证提交过来的数据是否在数据库里面已经存了，是否长度超长 等。。。
     * @param $obj 是controller对象
     * @param $Request 是需要验证的表单 比如 new OperatorsRequest()表单
     * @return mixed
     */
    static function validateRequest ($obj,$Request){
        if ($Request){
            $res = Validator::make($obj->request->all(),
                $Request->rules(),
                $Request->messages(),
                $Request->attributes()
            );
            if ($res->messages()->getMessages()){
                return MyUtils::dealFD($res->messages()->getMessages(),-1);
            }
        }
    }
    /**
     * 随机生成的文件名
     * @param int $len 生成的长度
     * @return bool|string
     */
    public static function randName($len = 6)
    {
        $str = 'abcdefghijkmnpqrstwxyz23456789';
        return substr(str_shuffle($str), 0, $len);
    }
    /**
     * 随机生成的文件名 不包括 数字，适用于生成变量名称
     * @param int $len 生成的长度
     * @return bool|string
     */
    public static function randNameStr($len = 6)
    {
        $str = 'abcdefghijkmnpqrstwxyz';
        return substr(str_shuffle($str), 0, $len);
    }
    /**
     * 随机生成的数字
     * @param int $len 生成的长度
     * @return bool|string
     */
    public static function randMath($len = 6)
    {
        $str = '012345678901234567890123456789012345678901234567890123456789';
        return substr(str_shuffle($str), 0, $len);
    }
    /**
     * 写日志
     * @param $path 会自动生成的路径
     * @param $str 需要写入的字符串
     */
    public static function writeToPathLog($path,$str)
    {
        $path = "./logs/".$path."/";
        if (!file_exists($path)) {
            self::mkdirs($path);
        }
        $files = scandir($path);
        // 删掉两个默认的数组
        if(in_array(".",$files)) unset($files[0]);
        if(in_array("..",$files))unset($files[1]);
        if ($files) {//有，表示不是第一次写日志
            $i = 0;
            foreach ($files as $k => $v) {
                if (filesize($path.$v) < 1 * 1024) {//文件大小上限为1k
                    $i++;
                    $name = $v;
                    break;
                }
            }
            if ($i == 0) {//表示需要新增一个文件，将原来的文件复制为备份，现在的文件名字仍然为logs
                $name = 'logs';
                copy($path . $name, $path . $name . count($files));//拷贝
                file_put_contents($path . $name, '');//新建一个logs文件
            }
        } else {//没有文件
            $name = "logs";
            file_put_contents($path . $name, '', FILE_APPEND);//新建一个文件
        }
        $res = json_decode(file_get_contents($path . $name), true);
        if ($res == null) {
            $res = [];
        }
        if(is_array($str)) $res = json_encode(array_merge($res, $str));
        else $res = $str;
        file_put_contents($path . $name, $res,FILE_APPEND);
    }
    /**
     * 新建文件夹，实现级联创建文件夹
     * @param unknown $dir
     * @param number $mode
     * @return boolean
     */
    public static function mkdirs($dir, $mode = 0777)
    {
        $res = @mkdir($dir, $mode, true);
        @chmod($dir,$mode);
        return $res;
    }
    /**
     * 将exel的内容转换成适合的数组
     * @param $getExel
     * @param int $key 表示exel表格从第几行开始算起 第一行是中文标题  就是0，第二行开始就是1
     * @return array
     */
    public static function exel2Array($getExel,$key = 0){
        $res = $getExel;
        $array1 = $getExel[$key];
        for($k=0;$k<=$key;$k++){
            unset($getExel[$k]);
        }
        $array2 = $getExel;
        foreach ($array2 as $k=>$v){
            $row = array_filter($v->all());
            if ($row){
                $res[] = array_combine($array1->all(),$v->all());
            }
        }
        return $res;
    }
    /**
     * 将exel的中文 键名 内容转换成字符串 键名 的数组
     * @param $getExel
     * @param $array_key 是 中文 与英文的数组对
     * @return mixed
     */
    public static function exel2Array_($getExel,$array_key){
        $attributes_new = [];
        $array_key = array_flip($array_key) ;//数组健值互换
        foreach($getExel as $kk=>$attributes)
        {
            foreach($attributes as $k=>$v)
            {
                if (isset($array_key[$k]))$attributes_new[$kk][$array_key[$k]] = $v;
            }
        }
        return $attributes_new;
    }
    /**
     * 查询session里面的用户 ,组的id
     * 当 idName = "group_id"表示的意思是组的id
     * @param $request
     * @param $idName 获取的健值
     * @param bool $unset 默认会自动消掉 获取的健值
     * @return mixed|string
     */
    public static function getSessionId(& $request,$idName,$unset=true){
        $id = MyUtils::sg($request,$idName);
        if (!$id){
            $id = Session::get($idName);
        }
        if ($unset)unset($request[$idName]);
        if (!$id){
            return false;
        }
        return $id;
    }
    /**
     * 查询session里面的用户 ,组的id
     * 当 idName = "group_id"表示的意思是组的id
     * @param $request
     * @param $idName 获取的健值
     * @param bool $unset 默认不会自动消掉 获取的健值
     * @return mixed|string
     */
    public static function getSessionIdKeep(& $request,$idName,$unset=true){
        $id = MyUtils::sg($request,$idName,'');
        if (!$id){
            $id = Session::get($idName);
        }
        if (!$id){
            return '';
        }
        return $id;
    }
    /**
     * 加入查询历史记录，作为搜索引擎用
     * @param $table
     * @param string $type 类似 csyys:18等
     * @param string $keyword
     * @return bool|int
     */
    public static function insertSearchHistory($table,$type="",$keyword = ''){
        if (!$keyword){
            $keyword = Globals::$request->name;
        }
        // 添加搜索的历史记录
        $keywordCount = SearchHistory::where("keyword",$keyword)->where('type',$type)->get("times")->first();
        if ($keywordCount){// 以前有人搜索过
            // 搜索记录 搜索的 次数+1
            $res = SearchHistory::where("keyword",$keyword)->where('type',$type)->update(['times'=>$keywordCount->times+1]);
        }else{
            $data['keyword'] = $keyword;
            $data['times'] = 1;
            $data['type'] = $type;
            $res = SearchHistory::insert($data);
        }
        activity()->causedBy(MyUtils::getSessionIdKeep(Globals::$request,'user_id'))
            ->inLog($type)
            ->performedOn($table)
            ->log("搜索");
        return $res;
    }

    /**
     * 拿到所有的子节点的id 返回的不是数组
     * @param $parentId 所要查询的所有子的 父id
     * @param $tableName 所要查询的表名字
     * @return true
     */
    public static function getAllChildrenId($parentId,$tableName){
        $tableName = env("DB_PREFIX").$tableName;
        $sql = "select id from (
              select t1.id,
              concat(',',if(find_in_set(parent_id, @pids) > 0, @pids := concat(@pids, ',', id), 0),',') as ischild
              from (
                   select id,parent_id from $tableName t order by parent_id, id
                  ) t1,
                  (select @pids := $parentId ids) t2
             ) t3 where isChild like \"%,$parentId,%\"";
        $res = self::dbSelect($sql,[]);
        return $res;
    }

    /**
     * 处理数组里面的数组为字符串 以逗号隔开
     * @param $arr
     * $subLastStr 去掉最后一个逗号
     * @param bool $subLastStr
     * @return false|string
     */
    public static function implodeArray($arr,$subLastStr = true){
        $res = "";
        foreach ($arr as $k=>$v){
            if (is_array($v)){
                $res .= '\''.array_values($v)[0].'\',';
            }
            if (is_object($v)){
                $res .=  '\''.array_values(get_object_vars($v))[0].'\',';
            }
        }
        if ($subLastStr){
            return substr($res,0,-1);
        }
        return $res;
    }
    /**
     * 拿到所有的子节点的id 返回的是数组
     * @param $parentId 所要查询的所有子的 父id
     * @param $tableName 所要查询的表名字
     * @return true
     */
    public static function getAllChildrenIds($parentId,$tableName){
        if (strpos($tableName,env("DB_PREFIX"))===false){
            $tableName = env("DB_PREFIX").$tableName;
        }
        $sql = "select id from (
              select t1.id,
              concat(',',if(find_in_set(parent_id, @pids) > 0, @pids := concat(@pids, ',', id), 0),',') as ischild
              from (
                   select id,parent_id from $tableName t order by parent_id, id
                  ) t1,
                  (select @pids := $parentId ids) t2
             ) t3 where isChild like \"%,$parentId,%\"";
        $res = self::dbSelect($sql,[],true);
        return $res;
    }
    /**
     * 拿到所有的子节点的id 返回的是数组
     * @param $parentId 所要查询的所有子的 父id
     * @param $tableName 所要查询的表名字
     * @return true
     */
    public static function getAllChildrenIds2($parentId,$tableName){
        if (strpos($tableName,env("DB_PREFIX"))===false){
            $tableName = env("DB_PREFIX").$tableName;
        }
        $sql = "select id from (
              select t1.id,
              concat(',',if(find_in_set(parent_id, @pids) > 0, @pids := concat(@pids, ',', id), 0),',') as ischild
              from (
                   select id,parent_id from $tableName t order by parent_id, id
                  ) t1,
                  (select @pids := $parentId ids) t2
             ) t3 where isChild like \"%,$parentId,%\"";
        $res = self::dbSelect($sql,[],true);
        $res = self::obj2arr($res);
        $finalRes = $res;
        if (is_array($res) && count($res)>0){
            foreach ($res as $k=>$v){
                $temp = self::getAllChildrenIds2($v['id'],$tableName);
                if (is_array($temp) && count($temp)>0){
                    $finalRes = array_merge($finalRes,$temp);
                }
            }
            return $finalRes;
        }else{
            return $res;
        }
    }

    /**
     * 拿到所有的子节点的id通过地址 返回的是数组
     * @param $parentId 所要查询的所有子的 父id
     * @param $tableName 所要查询的表名字
     * @param $address
     * @return true
     */
    public static function getAllChildrenIdsAddress($parentId,$tableName,&$address){
        if (strpos($tableName,env("DB_PREFIX"))===false){
            $tableName = env("DB_PREFIX").$tableName;
        }
        $sql = "select id from (
              select t1.id,
              concat(',',if(find_in_set(parent_id, @pids) > 0, @pids := concat(@pids, ',', id), 0),',') as ischild
              from (
                   select id,parent_id from $tableName t order by parent_id, id
                  ) t1,
                  (select @pids := $parentId ids) t2
             ) t3 where isChild like \"%,$parentId,%\"";
        $res = self::dbSelect($sql,[],true);
        if (is_array($res) && count($res)>0){
            foreach ($res as $k=>$v){
                $address = array_merge($address,self::getAllChildrenIdsAddress($v->id,$tableName,$address));
            }
        }else{
            return $res;
        }
        return $address;
    }
    /**
     * sql加密id
     * @param $columnValue
     * @return string
     */
    public static function encodeColumn($columnValue){
        if (env("ID_ENCODE_OPEN") && is_numeric($columnValue)){
            return DecodeInt::getInstance()->idToString($columnValue);
        }else{
            return $columnValue;
        }
    }
    /**
     * sql解密 id
     * @param $columnValue
     * @return string
     */
    public static function decodeColumn($columnValue){
        if (env("ID_ENCODE_OPEN") && !is_numeric($columnValue)){
            return DecodeInt::getInstance()->stringToId($columnValue);
        }else{
            return $columnValue;
        }
    }
    /**
     * 设置 request
     * @param $request 请求
     * @param $key 建
     * @param $val 值
     */
    public static function setRequest(&$request,$key,$val){
        $request->request->set($key,$val);
        $request->$key = $val;
        Globals::$request = $request;
    }

    /**
     * 将url字符串统一转换成数组的类型 比如 "http://asdas" 转换成 "["https://asdasd"]"
     * @param $data 要转化的数组
     * @param $columnName 列名
     * @return mixed
     */
    public static function setUrlToArrayStr($data,$columnName){
        foreach ($data as $k=>$v){
            $temp = MyUtils::sg($v,$columnName);
            $tempArr = json_decode($temp,true);
            if (!$tempArr){// 转换失败
                MyUtils::ss($data[$k],$columnName,"[\"".$temp."\"]");
            }
        }
        return $data;
    }

    /**
     * 设置 request
     * @param $key 建
     * @param $val 值
     */
    public static function setGlobalRequest($key,$val){
        Globals::$request->request->set($key,$val);
        Globals::$request->$key = $val;
    }
    /**
     * 清除 request
     * @param $request 请求
     * @param $key 建
     */
    public static function removeRequest(&$request,$key){
        $request->request->remove($key);
        $request->query->remove($key);
        unset($request->$key);
    }
    /**
     * 拿到所有的父亲节点的id
     * @param $id
     * @param $tableName 所要查询的表名字
     * @return true
     */
    public static function getAllParentId($id,$tableName){
        $tableName = env("DB_PREFIX").$tableName;
        $sql = "select id from (
              select t1.id,
              concat(',',if(find_in_set(parent_id, @pids) > 0, @pids := concat(@pids, ',', id), 0),',') as ischild
              from (
                   select id,parent_id from $tableName t order by parent_id, id
                  ) t1,
                  (select @pids := $id ids) t2
             ) t3 where isChild like \"%,$id,%\"";
        $res = self::dbSelect($sql,[]);
        return $res;
    }

    /**
     * 拿到所有的子节点的id 返回的是数组
     * @param $parentId 所要查询的所有子的 父id
     * @param $tableName 所要查询的表名字
     * @return true
     */
    public static function getAllParentIds2($parentId,$tableName){
        if (strpos($tableName,env("DB_PREFIX"))===false){
            $tableName = env("DB_PREFIX").$tableName;
        }
        $sql = "select id from (
              select t1.id,
              concat(',',if(find_in_set(parent_id, @pids) > 0, @pids := concat(@pids, ',', id), 0),',') as ischild
              from (
                   select id,parent_id from $tableName t order by parent_id, id
                  ) t1,
                  (select @pids := $parentId ids) t2
             ) t3 where isChild like \"%,$parentId,%\"";
        $res = self::dbSelect($sql,[],true);
        $res = self::obj2arr($res);
        $finalRes = $res;
        if (is_array($res) && count($res)>0){
            foreach ($res as $k=>$v){
                $temp = self::getAllParentIds2($v['id'],$tableName);
                if (is_array($temp) && count($temp)>0){
                    $finalRes = array_merge($finalRes,$temp);
                }
            }
            return $finalRes;
        }else{
            return $res;
        }
    }


    /**
     * 查询session里面的数据
     * 当 idName = "group_id"表示的意思是组的id
     * $type wy 表示是查询物业的  csyys 表示是城市运营商
     * @param $name
     * @param string $type
     * @param string $columnName
     * @param string $columValue
     * @return mixed|string
     */
    public static function getSessionConfig($name,$type="wy",$columnName = '',$columValue=''){
        $res = Redis::get('my_config:SESSION_TEMP_'.$name);
        if ($res){
            return $res;
        }else{
            $res =  getConfig($name,$type,$columnName,$columValue);
            Redis::set('my_config:SESSION_TEMP_'.$name,$res);
            $configs = json_decode(Redis::get('my_config:SESSION_TEMP_CONFIG'));
            if ($configs){
                Redis::set('my_config:SESSION_TEMP_CONFIG',json_encode(array_merge(['SESSION_TEMP_'.$name],$configs)));
            }else{
                Redis::set('my_config:SESSION_TEMP_CONFIG',json_encode(['SESSION_TEMP_'.$name]));
            }
            return $res;
        }
    }

    /**
     * 设置config 设置配置
     * @param $request 配置 传递的是数组 比如["PROJECT_ID"=>"wi6x1uhzw0"]
     * @param string $type
     * @param string $columnName
     * @param string $columValue
     * @return bool
     */
    public static function setDbConfig($request,$type="wy",$columnName = '',$columValue=''){
        $res = true;
        if ($request){
            $request = $request->request->all();
            if($request['project_id'])unset($request['project_id']);
            foreach ($request as $k=>$v){
                if ($columnName){
                    $ifexist = DB::table($type."_config")->where("name",$k)->where($columnName,$columValue)->count();// 是否存在
                    if($ifexist){
                        $res = $res && (DB::table($type."_config")->where("name",$k)->where($columnName,$columValue)->update(["content"=>$v])>=0);
                    }else{
                        $res = $res && (DB::table($type."_config")->where($columnName,$columValue)->insert(["name"=>$k,"content"=>$v,$columnName=>$columValue])>=0);
                    }
                }else{
                    $ifexist = DB::table($type."_config")->where("name",$k)->count();// 是否存在
                    if($ifexist){
                        $res = $res && (DB::table($type."_config")->where("name",$k)->update(["content"=>$v])>=0);
                    }else{
                        $res = $res && (DB::table($type."_config")->insert(["name"=>$k,"content"=>$v])>=0);
                    }
                }
            }
        }
        return $res;
    }

    /**
     * 得到唯一的code 订单的编号 32 位
     * @param $class
     * @param string $name
     * @return string
     */
    public static function getUniqueCode($class,$name='code'){
        // 得到唯一的openId
        $code = date('Ymdhms').MyUtils::randMath(6);
        $codeCount = $class::where($name,$code)->get()->count();
        // 不为空 再生成一遍
        if ($codeCount) $code = self::getUniqueCode($class);
        return $code;
    }

    /**
     * 生产商户订单
     * @return true
     */
    public static function getOrderNumber(){
        $yCode = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
        $dingdanhao = $yCode[intval(date('Y')) - 2011] . strtoupper(dechex(date('m'))) . date('d') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
        return $dingdanhao;
    }

    /**
     * 生成 埋点的type方便定位是哪个
     * @param int $wy_operator_id 运营商id
     * @param int $wy_property_id 物业id
     * @param int $wy_area_id 小区id
     * @return true
     */
    public static function getActivityLogType($wy_operator_id=0,$wy_property_id=0,$wy_area_id=0){
        if (!$wy_operator_id){
            $wy_operator_id = Session::get("manager_wy_operator_id");
            if (!$wy_operator_id){
                $wy_operator_id = Session::get("wy_operator_id");
            }
        }
        if (!$wy_property_id){
            $wy_property_id = Session::get("manager_wy_property_id");
            if (!$wy_property_id){
                $wy_property_id = Session::get("wy_property_id");
            }
        }
        if (!$wy_area_id){
            $wy_area_id = Session::get("manager_wy_area_id");
            if (!$wy_area_id){
                $wy_area_id = Session::get("wy_area_id");
                if (!$wy_area_id){
                    $area = Session::get("areas");
                    if ($area){
                        $wy_area_id = $area['id'];
                    }
                }
            }
        }
        return "csyys:[".$wy_operator_id."]wy:[".$wy_property_id."]area:[".$wy_area_id."]";
    }


    /**
     * 保存浏览量
     * @param $wyOperatorId 运营商id
     * @param $businessId 商家id
     * @param int $type 类型 1商品 2其他  访问类型，商品 等
     */
    public static function savePreviews($wyOperatorId,$businessId,$type=1){
        $temp['wy_operator_id'] = $wyOperatorId;
        $temp['business_id'] = $businessId;
        $temp['project_id'] = MyUtils::getSessionConfig("PROJECT_ID");
        $temp['type'] = $type;
        $one = Previews::where($temp)->get("*")->whereBetween("created_at",[DayTimes::getInstance()->beginToday(),DayTimes::getInstance()->endToday()])->first();
        if ($one){// 表示的是得到的 今天的 访问量的对象,有就 增加一次访问
            $one->times = ++$one->times;
            $one->save();
        }else{// 没有就新增一条
            $temp['times'] = 1;
            Previews::insertGetId($temp);
        }
    }

    /**
     * 保存浏览量
     * @param $request
     * @param $tableName
     */
    public static function saveTablePreviews($request,$tableName){
        if ($request->id){
            $temp['previews'] = 0;
            // 新增浏览量
            $first = $tableName::where("id",$request->id)->get("*")->first();
            if (isset($first->previews)){
                $previews = MyUtils::sg($first,"previews");
                $temp['previews'] = ++$previews;
            }
            if (isset($first->previews)){
                $previews = MyUtils::sg($first,"previews");
                $temp['previews'] = ++$previews;
            }
            $tableName::where("id",$request->id)->update($temp);
        }
    }

    /**
     * 检验公共的敏感字
     * @param $txt 输入的文字
     * @return mixed
     */
    public static function checkSensitive($txt){
        $words = '';
        if ($txt){
            $interference = ['&', '*'];
            $filename = public_path('uploads/sensitive/keywords'); //每个敏感词独占一行
            $sen = new Sensitive();
            $sen->interference($interference); //添加干扰因子
            $sen->addwords($filename); //需要过滤的敏感词
            $words = $sen->filter($txt);
        }
        return $words;
    }
    /**
     * 得到今天某个接口请求的次数
     * @param $type 类型 access_token 或者其他的
     * @param $uniqueKey 唯一的键
     * @return int
     */
    public function getTodayRequestTimes($type,$uniqueKey){
        // 得到access_token今天请求的次数
        $todayTimestamp = strtotime(DayTimes::getInstance()->beginToday());
        $key = "$type:$todayTimestamp:$uniqueKey";
        $getTimes = Redis::get($key)+0;
        Redis::set($key,$getTimes+1);// 设置access_token的获取次数加一
        return $getTimes;
    }

    /**
     * 解析唯一的token
     * @param $access_token 需要解析的 access_token
     * @return string
     */
    public function parseToken($access_token){
        if ($access_token){
            $expires_in = 7200;
            $code = MyUtils::decodeNew($access_token, env('APP_KEY'), $expires_in);
            if ($code){
                $res = mb_split(',',$code);
                $data['bucket_id'] = $res[0];
                $data['bucket_secret'] = $res[1];
                return MyUtils::dealFD($data,1);
            }else{
                return MyUtils::dealFD('对不起，您的access_token已经过期，请重新获取',-1);
            }
        }else{
            return MyUtils::dealFD("获取accessToken失败，请该秘钥的套件不存在，请进套件查看秘钥",-1);
        }
    }
}
